package za.org.jcicada.comms.impl;

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import za.org.jcicada.comms.IMessageListener;
import za.org.jcicada.comms.messages.CommsMessage;

public class MessageExecutor {
	
	private static final Log logger = LogFactory.getLog(MessageExecutor.class);
	private static final int NUM_THREADS = 1;
	private final ExecutorService executorService;
	private static MessageExecutor instance;
	
	/** 
	 * Singleton accessor method creates instance as necessary.
	 * @throws IOException if fails to create selector
	 */
	synchronized public static MessageExecutor getInstance() {
		if (instance == null) {
			instance = new MessageExecutor();
		}
		return instance;
	}
	
	private MessageExecutor() {
		executorService = Executors.newFixedThreadPool(NUM_THREADS);
	}
	
	/**
	 * The purpose of this class is to invoke the onMessage method on
	 * a listener with a given message in a Runnable class which can
	 * be submitted to an ExecutorService.
	 */
	public static class ListenerMessage implements Runnable {

		private IMessageListener listener;
		private CommsMessage message;
		
		public ListenerMessage(IMessageListener listener, CommsMessage message) {
			this.listener = listener;
			this.message = message;
		}
		
		@Override
		public void run() {
			try {
				listener.onMessage(message);
			} catch (Exception e) {
				logger.error(String.format("Message listener failed to process message ['%s']", message), e);
			}
		}

	}
	
	/**
	 * Submit a task to the executor service to pass the message to the
	 * listener in a worker thread.
	 * @param listener
	 * @param message
	 */
	public void submit(IMessageListener listener, CommsMessage message) {
		executorService.submit(new ListenerMessage(listener, message));
	}
	
	/**
	 * Shutdown the executor now if the instance exists and null the instance
	 * so that the next call to getInstance will create a new one.
	 */
	synchronized public static void shutdownNow() {
		if (instance != null) {
			instance.executorService.shutdownNow();
			instance = null;
		}
	}
	
}
